#ifndef _AMERICO_VECTOR4_H_
#define _AMERICO_VECTOR4_H_

#include <cassert>
#include <cmath>
#include "staticarray.h"

template <typename TYPE>
class Vector4
{
public:
    union
    {
        TYPE myX, x;
        TYPE myR, r;
    };
    union
    {
        TYPE myY, y;
        TYPE myG, g;
    };
    union
    {
        TYPE myZ, z;
        TYPE myB, b;
    };
    union
    {
        TYPE myW, w;
        TYPE myA, a;
    };

    Vector4();
    Vector4(const TYPE &aX, const TYPE &aY, const TYPE &aZ, const TYPE &aW);
    ~Vector4();

    Vector4 operator-() const;
    Vector4 operator+(const Vector4 &aVector) const;
    Vector4 operator-(const Vector4 &aVector) const;
    Vector4 operator*(const Vector4 &aVector) const;
    Vector4 operator*(const TYPE &aScalar) const;
    Vector4 operator/(const TYPE &aScalar) const;

    Vector4 &operator+=(const Vector4 &aVector);
    Vector4 &operator-=(const Vector4 &aVector);
    Vector4 &operator*=(const Vector4 &aVector);
    Vector4 &operator*=(const TYPE &aScalar);
    Vector4 &operator/=(const TYPE &aScalar);

	bool operator==(const Vector4 &aVector) const;
	bool operator!=(const Vector4 &aVector) const;

	Vector4 &Set(const TYPE &aX, const TYPE &aY, const TYPE &aZ, const TYPE &aW);

    float Length() const;
	float Length2() const;
    TYPE Dot(const Vector4 &aVector) const;

	Vector4 &Normalize();
};



template <typename TYPE>
Vector4<TYPE>::Vector4()
{
}

template <typename TYPE>
Vector4<TYPE>::Vector4(const TYPE &aX, const TYPE &aY, const TYPE &aZ, const TYPE &aW)
{
    myX = aX;
    myY = aY;
    myZ = aZ;
    myW = aW;
}

template <typename TYPE>
Vector4<TYPE>::~Vector4()
{
}

template <typename TYPE>
Vector4<TYPE> Vector4<TYPE>::operator-() const
{
    Vector4<TYPE> result(-myX, -myY, -myZ, -myW);
    return result;
}

template <typename TYPE>
Vector4<TYPE> Vector4<TYPE>::operator+(const Vector4<TYPE> &aVector) const
{
    Vector4<TYPE> result(*this);
    result += aVector;
    return result;
}

template <typename TYPE>
Vector4<TYPE> Vector4<TYPE>::operator-(const Vector4<TYPE> &aVector) const
{
    Vector4<TYPE> result(*this);
    result -= aVector;
    return result;
}

template <typename TYPE>
Vector4<TYPE> Vector4<TYPE>::operator*(const Vector4<TYPE> &aVector) const
{
    Vector4<TYPE> result(*this);
    result *= aVector;
    return result;
}

template <typename TYPE>
Vector4<TYPE> Vector4<TYPE>::operator*(const TYPE &aScalar) const
{
    Vector4<TYPE> result(*this);
    result *= aScalar;
    return result;
}

template <typename TYPE>
Vector4<TYPE> Vector4<TYPE>::operator/(const TYPE &aScalar) const
{
    Vector4<TYPE> result(*this);
    result /= aScalar;
    return result;
}

template <typename TYPE>
Vector4<TYPE> &Vector4<TYPE>::operator+=(const Vector4<TYPE> &aVector)
{
    myX += aVector.myX;
    myY += aVector.myY;
    myZ += aVector.myZ;
    myW += aVector.myW;
    return *this;
}

template <typename TYPE>
Vector4<TYPE> &Vector4<TYPE>::operator-=(const Vector4<TYPE> &aVector)
{
    myX -= aVector.myX;
    myY -= aVector.myY;
    myZ -= aVector.myZ;
    myW -= aVector.myW;
    return *this;
}

template <typename TYPE>
Vector4<TYPE> &Vector4<TYPE>::operator*=(const Vector4<TYPE> &aVector)
{
    myX *= aVector.myX;
    myY *= aVector.myY;
    myZ *= aVector.myZ;
    myW *= aVector.myW;
    return *this;
}

template <typename TYPE>
Vector4<TYPE> &Vector4<TYPE>::operator*=(const TYPE &aScalar)
{
    myX *= aScalar;
    myY *= aScalar;
    myZ *= aScalar;
    myW *= aScalar;
    return *this;
}

template <typename TYPE>
Vector4<TYPE> &Vector4<TYPE>::operator/=(const TYPE &aScalar)
{
	assert(aScalar!=0 && "Division by 0");
    myX /= aScalar;
    myY /= aScalar;
    myZ /= aScalar;
    myW /= aScalar;
    return *this;
}

template <typename TYPE>
bool Vector4<TYPE>::operator==(const Vector4<TYPE> &aVector) const
{
	return ((myX == aVector.myX) && (myY == aVector.myY) && (myZ == aVector.myZ) && (myW == aVector.myW));
}

template <typename TYPE>
bool Vector4<TYPE>::operator!=(const Vector4<TYPE> &aVector) const
{
	return ! operator==(aVector);
}

template <typename TYPE>
Vector4<TYPE> &Vector4<TYPE>::Set(const TYPE &aX, const TYPE &aY, const TYPE &aZ, const TYPE &aW)
{
	myX = aX;
	myY = aY;
	myZ = aZ;
	myW = aW;
	return *this;
}

template <typename TYPE>
float Vector4<TYPE>::Length() const
{
    return sqrt(myX*myX+myY*myY+myZ*myZ+myW*myW);
}

template <typename TYPE>
float Vector4<TYPE>::Length2() const
{
	return (myX*myX+myY*myY+myZ*myZ+myW*myW);
}

template <typename TYPE>
TYPE Vector4<TYPE>::Dot(const Vector4<TYPE> &aVector) const
{
    return (myX*aVector.myX+myY*aVector.myY+myZ*aVector.myZ+myW*aVector.myW);
}

template <typename TYPE>
Vector4<TYPE> &Vector4<TYPE>::Normalize()
{
    TYPE length = Length();
	if(length>0)
	{
		myX /= length;
		myY /= length;
		myZ /= length;
		myW /= length;
	}
	return *this;
}

#endif
